package org.gpp.proj1.ui.gui;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.swing.JOptionPane;

import org.gpp.proj1.logic.core.board.RobotPosition;
import org.gpp.proj1.logic.metadata.PartCategory;
import org.gpp.proj1.logic.metadata.RobotEntry;
import org.gpp.proj1.logic.metadata.RobotPartCategory;
import org.gpp.proj1.logic.profile.UnsufficientFundsException;
import org.gpp.proj1.map.GameMap;
import org.gpp.proj1.ui.ISRGUIView;
import org.gpp.proj1.ui.controller.ISRGUIController;
import org.gpp.proj1.ui.gui.command.SRGGUICommand;
import org.gpp.proj1.ui.gui.command.SRGGUICommandFactory;
import org.gpp.proj1.ui.gui.editor.RobotBuilderDialog;
import org.gpp.proj1.ui.gui.editor.RobotListEditorWindow;
import org.gpp.proj1.ui.gui.ingame.GameViewportManager;
import org.gpp.proj1.ui.gui.lobby.SCRClientServer;
import org.gpp.proj1.ui.gui.lobby.SCRLobby;
import org.gpp.proj1.ui.gui.profile.ProfileSelectionFrame;
import org.gpp.proj1.ui.gui.shop.BuyPartsWindow;

/**
 * SomeRobotGame UI - Graphical interface
 * 
 * @author Paolo Victor, paolovictor@gmail.com
 */
public class SRGGUIView implements ISRGUIView, ActionListener {

	private ISRGUIController controller;
	
	private ProfileSelectionFrame profileSelection;

	private BuyPartsWindow partShopWindow;

	private RobotListEditorWindow robotListEditorWindow;

	private RobotBuilderDialog robotBuilderFrame;

	private MainWindow mainWindow;

	private SCRLobby lobby;

	private InitStatusWindow initPhaseStatusWindow;

	private GameViewportManager gameViewportManager;

	private GameViewportExecutorThread runnerThread;
	
	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.view.ISRGUIView#notifyLoadProfile()
	 */
	public void notifyLoadProfile() {
		profileSelection = new ProfileSelectionFrame( this );
		profileSelection.setVisible( true );
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.view.ISRGUIView#setController(org.gpp.proj1.ui.controller.ISRGUIController)
	 */
	public void setController(ISRGUIController controller) {

		this.controller = controller;
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.view.ISRGUIView#notifyAborting(java.lang.String)
	 */
	public void notifyAborting(String message) {
		
		JOptionPane.showMessageDialog( null, message, "Error", JOptionPane.ERROR_MESSAGE );
	}

	/* (non-Javadoc)
	 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
	 */
	public void actionPerformed(ActionEvent e) {
		
		SRGGUICommand command = SRGGUICommandFactory.createCommand( this, e.getActionCommand() );
		command.execute();
	}

	public ISRGUIController getController() {
		
		return this.controller;
	}

	public Object getSelectedProfile() {

		return profileSelection.getSelectedProfile();
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.view.ISRGUIView#notifyLoadedProfile()
	 */
	public void notifyLoadedProfile() {
		
		profileSelection.dispose();
		
		this.mainWindow = new MainWindow( this );
		this.mainWindow.setVisible( true );
	}

	/**
	 * Refreshes the profiles list on the profile selection
	 * frame
	 */
	public void refreshProfiles() {
		
		this.profileSelection.refreshProfiles();
	}

	public void displayPartShop() {
		
		if( this.partShopWindow != null ) {
			this.partShopWindow.dispose();
		}
		
		this.partShopWindow = new BuyPartsWindow( this );
		this.partShopWindow.setVisible( true );
	}
	
	public void displayRobotListEditor() {
		
		if( this.robotListEditorWindow != null ) {
			this.robotListEditorWindow.dispose();
		}
		
		this.robotListEditorWindow = new RobotListEditorWindow( this );
		this.robotListEditorWindow.setVisible( true );
	}

	public void buySelectedPart() {
		
		PartCategory category = this.partShopWindow.getSelectedCategory();
		String part = this.partShopWindow.getSelectedPart();
		int quantity = this.partShopWindow.getPartQuantity();
		
		try {
			this.controller.buyPart( category, part, quantity );
			this.partShopWindow.updatePlayerData();
		} catch (UnsufficientFundsException e) {
			JOptionPane.showMessageDialog( null, "You don't have 	" +
					"enough cash to complete this operation", "Error", JOptionPane.ERROR_MESSAGE );
		}
	}

	public void displayRobotCreator() {
		
		this.robotBuilderFrame = new RobotBuilderDialog( this );
		this.robotBuilderFrame.setVisible( true );
	}
	
	public void displayRobotEditor() {
		
		if( robotListEditorWindow.getSelectedRobot() == null ) {
			JOptionPane.showMessageDialog( null, "There's no selected robot", "Error", JOptionPane.ERROR_MESSAGE );
		} else {		
			this.robotBuilderFrame = new RobotBuilderDialog( this, robotListEditorWindow.getSelectedRobot() );
			this.robotBuilderFrame.setVisible( true );
		}
	}

	public void notifyRobotSaved() {
		
		this.controller.saveRobot( robotBuilderFrame.getOldRobotName(), robotBuilderFrame.getNewRobotName(), robotBuilderFrame.getSelections() );
		
		this.robotListEditorWindow.update();
	}

	public List<RobotEntry> getPlayerRobots() {
		
		return this.controller.getPlayerRobots();
	}

	public void notifyRobotDeleted() {

		if( robotListEditorWindow.getSelectedRobot() == null ) {
			JOptionPane.showMessageDialog( this.robotListEditorWindow, "There's no selected robot", "Error", JOptionPane.ERROR_MESSAGE );
		} else {
			
			this.controller.deleteRobot( robotListEditorWindow.getSelectedRobot() );
			
			this.robotListEditorWindow.update();
		}
	}

	public void showStartGameWindow() {
		
		if( this.robotListEditorWindow != null ) {
			this.robotListEditorWindow.dispose();
		}
		
		if( this.partShopWindow != null ) {
			this.partShopWindow.dispose();
		}
		
		this.mainWindow.setVisible( false );

		if( this.getController().isClientConnected() ) {		

			lobby = new SCRLobby( this, this.controller.isServer() );
			lobby.setVisible( true );
			
		} else {

			SCRClientServer cs = new SCRClientServer( this );
			cs.setVisible( true );
		}
	}

	public void showMainWindow() {
		
		if( this.mainWindow != null ) {
			this.mainWindow.setVisible( true );
		}
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.ISRGUIView#notifyChatMessage(java.lang.String, java.lang.String)
	 */
	public void notifyChatMessage(String alias, String chatMessage) {
		
		if( this.lobby != null ) {
			this.lobby.notifyChatMessage(alias, chatMessage);
		}
		
		if( this.controller.isClientConnected() && this.gameViewportManager != null ) {
			
			this.gameViewportManager.notifyChatMessage( alias, chatMessage );
		}
	}

	public void renewLobby(SCRLobby lobby) {
		
		this.lobby = lobby;
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.ISRGUIView#notifyWhosHereMessage(java.util.Map, java.util.Set)
	 */
	public void notifyWhosHereMessage(Map<String, String> aliasMap, Set<String> readyClientsSet) {

		if( this.lobby != null ) {
			this.lobby.notifyWhosHereMessage(aliasMap, readyClientsSet);
		}
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.ISRGUIView#notifyWhosHereMessage(java.lang.String)
	 */
	public void notifyWhosHereMessage(String mapName) {
		
		if( this.lobby != null ) {
			this.lobby.notifyMapChanged(mapName);
		}
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.ISRGUIView#notifyServerName(java.lang.String)
	 */
	public void notifyServerName(String serverName) {
	
		if( this.lobby != null ) {
			this.lobby.notifyServerName(serverName);
		}
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.ISRGUIView#notifyDisconnected()
	 */
	public void notifyDisconnected() {

		if( this.lobby != null ) {
			this.lobby.dispose();
		}
		
		this.showMainWindow();
		
		JOptionPane.showMessageDialog( this.mainWindow, "Disconnected from server", "Disconnected", JOptionPane.WARNING_MESSAGE );
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.ISRGUIView#notifyClientDisconnected(java.lang.String)
	 */
	public void notifyClientDisconnected(String clientId) {
		
		 if( this.lobby != null ) {
			this.lobby.notifyClientDisconnected( clientId );
		}
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.ISRGUIView#notifyInitPhaseFailed(java.lang.String)
	 */
	public void notifyInitPhaseFailed(String cause) {

		if( this.initPhaseStatusWindow != null ) {
			this.initPhaseStatusWindow.setVisible( false );
		}
		
		JOptionPane.showMessageDialog( this.mainWindow, "Init phase failed. Reason: " + cause, "Init failed", JOptionPane.WARNING_MESSAGE );
		
		this.showMainWindow();
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.ISRGUIView#notifyInitPhaseStarted()
	 */
	public void notifyInitPhaseStarted() {
		
		if( this.lobby != null ) {
			this.lobby.setVisible( false );
		}
		
		this.initPhaseStatusWindow = new InitStatusWindow();
		this.initPhaseStatusWindow.setVisible( true );
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.ISRGUIView#updateInitStatus(java.lang.String)
	 */
	public void updateInitStatus(String message) {
		
		if( this.initPhaseStatusWindow != null ) {
			this.initPhaseStatusWindow.updateStatus( message );
		}
	}

	/* (non-Javadoc)
	 * @see org.gpp.proj1.ui.ISRGUIView#gameHasStarted(org.gpp.proj1.map.GameMap, java.util.Map, java.util.Map)
	 */
	public void gameHasStarted(GameMap map, Map<String, Map<String, RobotPosition>> positions, Map<String, Map<String, Map<RobotPartCategory, String>>> robots) {

		if( this.initPhaseStatusWindow != null ) {
			this.initPhaseStatusWindow.setVisible( false );
		}
		
		this.gameViewportManager = new GameViewportManager( this.controller, map, positions, robots );
				
		this.runnerThread = new GameViewportExecutorThread( this.gameViewportManager );
		new Thread( this.runnerThread ).start();
	}
	
	public void notifyCurrentTurn(String clientId, String playerAlias) {

		if( this.gameViewportManager != null ) {
			this.gameViewportManager.notifyCurrentTurn( clientId, playerAlias );
		}
	}

	public void notifyYourTurn() {
		
		if( this.gameViewportManager != null ) {
			this.gameViewportManager.notifyYourTurn();
		}
	}
	
	public void notifyTurnRobot(String robotName) {
		
		if( this.gameViewportManager != null ) {
			this.gameViewportManager.notifyTurnRobot(robotName);
		}		
	}
	
	public void notifyRobotMoved(String player, String robot, int x, int y) {
		
		if( this.gameViewportManager != null ) {
			this.gameViewportManager.notifyRobotMoved(player, robot, x, y);
		}
	}
	
	public void notifyRobotAttacked(String player, String robot, int x, int y, String part, String attack) {

		if( this.gameViewportManager != null ) {
			this.gameViewportManager.notifyRobotAttacked(player, robot, x, y, part, attack);
		}
	}
	
	private class GameViewportExecutorThread implements Runnable {

		private final GameViewportManager gameViewportManager;

		public GameViewportExecutorThread(GameViewportManager manager) {			
			this.gameViewportManager = manager;
		}

		public void run() {
			this.gameViewportManager.start();			
		}
		
	}
}
